#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include "clsv.h"
#include "hton.h"


/* Set of functions than convert structures defined in cliser.h to a stream
 * Each data type is set from host to network order
 */
static int hton_clsv_passwd(void * p, void ** stream);
static int hton_clsv_get_ticket(void * p, void ** stream);
static int hton_clsv_use_ticket(void * p, void ** stream);
static int hton_clsv_upload(void * p, void ** stream);
static int hton_clsv_list_categories(void * p, void ** stream);
static int hton_clsv_list_users(void * p, void ** stream);
static int hton_clsv_list_files(void * p, void ** stream);

static int hton_svcl_passwd(void * p, void ** stream);
static int hton_svcl_get_ticket(void * p, void ** stream);
static int hton_svcl_upload(void * p, void ** stream);

static int hton_comm_data_block(void * p, void ** stream);
static int hton_comm_list_block(void * p, void ** stream);
static int hton_comm_status(void * p, void ** stream);


static int (*hton_clsv[])(void * p, void ** stream) =
{
	hton_clsv_passwd,
	hton_clsv_get_ticket,
	hton_clsv_use_ticket,
	hton_clsv_upload,
	hton_clsv_list_categories,
	hton_clsv_list_users,
	hton_clsv_list_files
};

static int (*hton_svcl[])(void * p, void ** stream) =
{
	hton_svcl_passwd,
	hton_svcl_get_ticket,
	hton_comm_data_block,
	hton_svcl_upload,
	hton_comm_list_block,
	hton_comm_list_block, 
	hton_comm_list_block,
};

static int (*hton_comm[])(void * p, void ** stream) = 
{
	hton_comm_data_block,
	hton_comm_data_block,
	hton_comm_data_block,
	hton_comm_data_block,
	hton_comm_list_block,
	hton_comm_list_block,
	hton_comm_list_block
};

int
hton_clsv_buffer(CLSV_BUFFER * p, void ** stream, OPC opcode, int src)
{
	void * buffer, * structure, * auxP;
	char * offset;
	int mem, qty;
	uint16_t aux;
	
	/* determine direction of transfer */
	switch (src)
	{
		case CL:
			mem = (*hton_clsv[opcode])((void *)&(p->data), &structure);
			break;
		case SV:
			mem = (*hton_svcl[opcode])((void *)&(p->data), &structure);
			break;
		case COMM:
			mem = (*hton_comm[opcode])((void *)&(p->data), &structure);
			break;
		default:
			fprintf(stderr, "Not called from server or client\n");
	}
	
	qty = mem + sizeof(aux);	/* bytes in of stream */
	buffer = *stream;			/* pointer to stream */
	
	/* copy opcode to stream, network byte order */
	aux = htons(opcode);
	auxP = structure;
	memcpy(buffer, (void *)&aux, sizeof(aux));
	
	/* move pointer and copy struct converted to stream, network byte order */
	offset = (char *)buffer+sizeof(aux);
	memcpy((void *)offset, structure, mem);
	free(structure);
	
	return qty;
}

static int
getmemsize(int * array, int len)
{
	int i, ret = 0;
	for (i=0; i<len; i++)
		ret += array[i];
	return ret;
}

static int
createstream(void * stream, int * size, char ** pointers, int len)
{
	int i, bytes;
	char * offset;
	
	for (i=0, bytes=0; i<len; i++)
	{
		offset = (char *)stream + bytes;
		memcpy((void *)offset, (void *)pointers[i], size[i]);
		bytes += size[i];
	}

	return bytes;
}

static int
hton_clsv_passwd(void * p, void ** stream)
{
	CLSV_PASSWD * p2 = p;
	int ssize[] = {MAX_USER+1, MAX_EPASS, MAX_EPASS};
	char * pointers[] = {p2->user, p2->oldpass, p2->newpass};
	int len = sizeof(pointers)/sizeof(pointers[0]);
	
	if ((*stream = malloc(getmemsize(ssize, len))) == NULL)
	{
		fprintf(stderr, "Memory allocation error\n");
		return -1;
	}
	
	return createstream(*stream, ssize, pointers, len);
}

static int
hton_clsv_get_ticket(void * p, void ** stream)
{
	CLSV_GET_TICKET * p2 = p;
	int ssize[] = {MAX_USER+1, MAX_EPASS, MAX_EFILENAME};
	char * pointers[] = {(char *) p2->user, (char *) p2->pass, 
							(char *)p2->filename};
	int len = sizeof(pointers)/sizeof(pointers[0]);
	
	if ((*stream = malloc(getmemsize(ssize, len))) == NULL)
	{
		fprintf(stderr, "Memory allocation error\n");
		return -1;
	}
	
	return createstream(*stream, ssize, pointers, len);
}


static int
hton_clsv_use_ticket(void * p, void ** stream)
{
	CLSV_USE_TICKET * p2 = p;
	int ssize[] = {MAX_USER+1, MAX_EPASS, MAX_ETICKET};
	char * pointers[] = {p2->user, p2->pass, p2->ticket_id};
	int len = sizeof(pointers)/sizeof(pointers[0]);
	
	if ((*stream = malloc(getmemsize(ssize, len))) == NULL)
	{
		fprintf(stderr, "Memory allocation error\n");
		return -1;
	}
	
	return createstream(*stream, ssize, pointers, len);
}

static int
hton_clsv_upload(void * p, void ** stream)
{
	CLSV_UPLOAD * p2 = p;
	uint32_t size = htonl(p2->size);
	int ssize[] = {MAX_FILENAME+1, sizeof(size), MAX_CATEGORY+1};
	char * pointers[] = {p2->filename, (char *) &size, p2->category};
	int len = sizeof(pointers)/sizeof(pointers[0]);
	
	if ((*stream = malloc(getmemsize(ssize, len))) == NULL)
	{
		fprintf(stderr, "Memory allocation error\n");
		return -1;
	}
	
	return createstream(*stream, ssize, pointers, len);
}

static int
hton_clsv_list_categories(void * p, void ** stream)
{
	printf("hton_clsv_list_categories\n");
	if ((*stream = malloc(0)) == NULL)
	{
		fprintf(stderr, "Memory allocation error\n");
		return -1;
	}
	
	return 0;
}

static int
hton_clsv_list_users(void * p, void ** stream)
{
	CLSV_LIST_USERS * p2 = p;
	
	int ssize[] = {MAX_USER+1, MAX_EPASS};
	char * pointers[] = {(char *)p2->user, (char *)p2->pass};
	int len = sizeof(pointers)/sizeof(pointers[0]);
	
	if ((*stream = malloc(getmemsize(ssize, len))) == NULL)
	{
		fprintf(stderr, "Memory allocation error\n");
		return -1;
	}
	
	return createstream(*stream, ssize, pointers, len);
}

static int
hton_clsv_list_files(void * p, void ** stream)
{
	CLSV_LIST_FILES * p2 = p;
	int ssize[] = {MAX_USER+1, MAX_EPASS, MAX_CATEGORY+1};
	char * pointers[] = {(char *)p2->user, (char *)p2->pass, (char *) 
	p2->category};
	int len = sizeof(pointers)/sizeof(pointers[0]);
	
	if ((*stream = malloc(getmemsize(ssize, len))) == NULL)
	{
		fprintf(stderr, "Memory allocation error\n");
		return -1;
	}
	
	return createstream(*stream, ssize, pointers, len);
}


static int
hton_comm_data_block(void * p, void ** stream)
{
	COMM_DATA_BLOCK * p2 = p;
	uint16_t status = htons(p2->status);
	int ssize[] = {sizeof(status), MAX_DATA};
	char * pointers[] = {(char *)&status, p2->data};
	int len = sizeof(pointers)/sizeof(pointers[0]);
	
	if ((*stream = malloc(getmemsize(ssize, len))) == NULL)
	{
		fprintf(stderr, "Memory allocation error\n");
		return -1;
	}
	
	return createstream(*stream, ssize, pointers, len);
}

static int
hton_comm_list_block(void * p, void ** stream)
{
	COMM_LIST_BLOCK * p2 = p;
	uint16_t status = htons(p2->status);
	int ssize[] = {sizeof(status), MAX_LIST};
	char * pointers[] = {(char *)&status, (char *)p2->data};
	int len = sizeof(pointers)/sizeof(pointers[0]);
	
	if ((*stream = malloc(getmemsize(ssize, len))) == NULL)
	{
		fprintf(stderr, "Memory allocation error\n");
		return -1;
	}
	
	return createstream(*stream, ssize, pointers, len);
}

static int
hton_comm_status(void * p, void ** stream)
{
	COMM_STATUS * p2 = p;
	uint16_t status = htons(p2->status);
	int ssize[] = {sizeof(status)};
	char * pointers[] = {(char *)&status};
	int len = sizeof(pointers)/sizeof(pointers[0]);
	
	if ((*stream = malloc(getmemsize(ssize, len))) == NULL)
	{
		fprintf(stderr, "Memory allocation error\n");
		return -1;
	}
	
	return createstream(*stream, ssize, pointers, len);
}

static int
hton_svcl_passwd(void * p, void ** stream)
{
	SVCL_PASSWD * p2 = p;
	uint16_t status = htons(p2->status);
	int ssize[] = {sizeof(status)};
	char * pointers[] = {(char *)&status};
	int len = sizeof(pointers)/sizeof(pointers[0]);
	
	if ((*stream = malloc(getmemsize(ssize, len))) == NULL)
	{
		fprintf(stderr, "Memory allocation error\n");
		return -1;
	}
	
	return createstream(*stream, ssize, pointers, len);
}

static int
hton_svcl_get_ticket(void * p, void ** stream)
{
	SVCL_GET_TICKET * p2 = p;
	uint16_t status = htons(p2->status);
	uint32_t dtime = htonl(p2->dtime);
	uint32_t etime = htonl(p2->etime);
	uint32_t ftime = htonl(p2->ftime);
	
	int ssize[] = {sizeof(status), sizeof(dtime), sizeof(etime), sizeof(ftime),
					MAX_ETICKET};
	char * pointers[] = {(char *)&status, (char *)&dtime, (char *)&etime,
						(char *)&ftime, p2->id};
	int len = sizeof(pointers)/sizeof(pointers[0]);
	
	if ((*stream = malloc(getmemsize(ssize, len))) == NULL)
	{
		fprintf(stderr, "Memory allocation error\n");
		return -1;
	}
	
	return createstream(*stream, ssize, pointers, len);
}

static int
hton_svcl_upload(void * p, void ** stream)
{
	SVCL_UPLOAD * p2 = p;
	uint16_t status = htons(p2->status);
	int ssize[] = {sizeof(status)};
	char * pointers[] = {(char *)&status};
	int len = sizeof(pointers)/sizeof(pointers[0]);
	
	if ((*stream = malloc(getmemsize(ssize, len))) == NULL)
	{
		fprintf(stderr, "Memory allocation error\n");
		return -1;
	}
	
	return createstream(*stream, ssize, pointers, len);
}
